TODO tratar de NAs e passar tabelas pra csv

For R beginners

New chunk Ctrl+Alt+I

Execute chunk Ctrl+Shift+Enter

Execute all chunks Ctrl+Alt+R

HTML preview Ctrl+Shift+K

Library preparations

library(readr)
library(dplyr)
library(tidyverse)
library(ggplot2)
library(reshape2)
library(stats)

Data Import

data <- read.csv("~/4year/2semester/dtII/CSVs/HEIs.csv",
                 colClasses = c(tweet_id = "character"))

# Modifying created_at type so that attribute can be used more easily 
data$created_at <- as.POSIXct(data$created_at,
                              format= "%Y-%m-%dT%H:%M:%S", tz="UTC")

#View(data)
summary(data)
      id              tweet_id             text               type           bookmark_count    favorite_count     retweet_count      reply_count      
 Length:11728       Length:11728       Length:11728       Length:11728       Min.   :  0.000   Min.   :    0.00   Min.   :   0.00   Min.   :   0.000  
 Class :character   Class :character   Class :character   Class :character   1st Qu.:  0.000   1st Qu.:    7.00   1st Qu.:   2.00   1st Qu.:   0.000  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median :  0.000   Median :   20.00   Median :   5.00   Median :   1.000  
                                                                             Mean   :  1.543   Mean   :   60.67   Mean   :  10.62   Mean   :   3.888  
                                                                             3rd Qu.:  1.000   3rd Qu.:   57.00   3rd Qu.:  11.00   3rd Qu.:   3.000  
                                                                             Max.   :418.000   Max.   :41655.00   Max.   :4214.00   Max.   :2317.000  
                                                                                                                                                      
   view_count        created_at                       hashtags             urls            media_type         media_urls       
 Min.   :      5   Min.   :2022-08-01 03:05:11.00   Length:11728       Length:11728       Length:11728       Length:11728      
 1st Qu.:   2643   1st Qu.:2022-10-19 12:56:27.00   Class :character   Class :character   Class :character   Class :character  
 Median :   6240   Median :2023-01-29 08:26:30.00   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
 Mean   :  14182   Mean   :2023-01-30 07:39:34.96                                                                              
 3rd Qu.:  16058   3rd Qu.:2023-05-05 14:16:43.25                                                                              
 Max.   :7604544   Max.   :2023-08-31 20:50:01.00                                                                              
 NA's   :4840                                                                                                                  

Initial Data Preparation

# View of how many entries each HEI has
number_interactions <- data %>%
              group_by(id) %>% summarise(count = n())

number_interactions
# Since complutense only has 1 entry we can't learn anything from it, so we removed it
data <- data[data$id != "complutense.csv", ]
# Visualization of number all posts, just tweets and just replys
number_posts <- data %>%
              group_by(id) %>% summarise(count = n())

number_tweets <- data[data$type == "Tweet", ] %>%
              group_by(id) %>% summarise(count = n())

number_replys <- data[data$type == "Reply", ] %>%
              group_by(id) %>% summarise(count = n())

print(number_posts)
print(number_tweets)
print(number_replys)

Calculating the percentage of tweets and replies based on all posts

# Merging the counts of tweets (count.y) and replies (count) with the count of posts (count.x)
data_ratio <- merge(number_posts, number_tweets, by = "id", all = TRUE)
data_ratio <- merge(data_ratio, number_replys, by = "id", all = TRUE)


data_ratio$percentage_tweets <- (data_ratio$count.y / data_ratio$count.x) * 100
data_ratio$percentage_replies <- (data_ratio$count / data_ratio$count.x) * 100

data_ratio <- data_ratio[, c("id", "percentage_tweets", "percentage_replies")]

print(data_ratio)

For now we’ll be only looking at tweets

data_tweets <- data[data$type == "Tweet", ]

data_tweets

Function to calculate average posts

average_tweets <- function(timeframe = "days"){
  # Calculation of the timeframe between earliest and latest post for each HEI
  date_range <- data_tweets %>%
    group_by(id) %>%
    summarise(min_date = min(created_at),
              max_date = max(created_at)) %>%
    mutate(num_days = as.numeric(difftime(max_date, min_date, units = timeframe)))
  
  # Naming the column respecting the timeframe
  column_name <- paste0("avg_tweets_per_", timeframe)
  
  # Calculation of the number of tweets per day for each HEI
  tweets_per_timeframe <- number_tweets %>%
    left_join(date_range, by = "id") %>%
    mutate(!!column_name := count / num_days)
  
  print(tweets_per_timeframe)
  return(tweets_per_timeframe)
}
tweets_per_day <- average_tweets()
tweets_per_week <- average_tweets(timeframe = "weeks")

Plot for the average number of tweets per day for each HEI

barplot(tweets_per_day$avg_tweets_per_days,
        names.arg = tweets_per_day$id,
        main = "Average Tweets per Day",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(tweets_per_day$avg_tweets_per_days) + 1),
        las = 2,
        col = "#3498DB")

# Adding text labels over each bar and aligning it with the center of each bar 
text(x = barplot(tweets_per_day$avg_tweets_per_days, plot = FALSE),
     y = tweets_per_day$avg_tweets_per_days,
     labels = round(tweets_per_day$avg_tweets_per_days, 2),
     pos = 3)

Plot for the average number of tweets per week for each HEI

barplot(tweets_per_week$avg_tweets_per_weeks,
        names.arg = tweets_per_week$id,
        main = "Average Tweets per Week",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(tweets_per_week$avg_tweets_per_weeks) + 5),
        las = 2,
        col = "#E74C3C")

text(x = barplot(tweets_per_week$avg_tweets_per_weeks, plot = FALSE),
     y = tweets_per_week$avg_tweets_per_weeks,
     labels = round(tweets_per_week$avg_tweets_per_weeks, 2),
     pos = 3)

Defining the intervals of time for the academic year

intervals <- list(
  interval1 = as.POSIXct(c("2022-08-31", "2022-12-15")),
  interval2 = as.POSIXct(c("2023-01-04", "2023-04-01")),
  interval3 = as.POSIXct(c("2023-04-14", "2023-06-15"))
)

Function to check if a date falls within a given interval of time and apply appropriate Boolean

check_interval <- function(date) {
  for (i in 1:length(intervals)) {
    interval_start <- intervals[[i]][1]
    interval_end <- intervals[[i]][2]
    if (date >= interval_start & date <= interval_end) {
      return(TRUE)
    }
  }
  return(FALSE)
}
data_tweets$academic_year <- sapply(data_tweets$created_at, check_interval)
print(data.frame(id = data_tweets$id, academic_year = data_tweets$academic_year))

Plot for the number of tweets per timeframe of either vacation or academic time

barplot(table(data_tweets$academic_year),
        main = "Number of Tweets per Timeframe",
        xlab = "Time",
        ylab = "Count",
        ylim = c(0, max(table(data_tweets$academic_year)) + 1000),
        names.arg = c("Vacation", "Academic"),
        col = c("#8E44AD", "#F1C40F"))

text(x = barplot(data_tweets$academic_year, plot = FALSE), 
     y = table(data_tweets$academic_year) + 0.5, 
     labels = table(data_tweets$academic_year), 
     pos = 3)

Function to count number of tweets and average per day

analyze_tweets <- function(academic_year_filter = TRUE) {
  # Filtering the data based on the academic_year_filter
  filtered_data <- data_tweets %>%
    filter(academic_year == academic_year_filter)
  
  # Count of days for each HEI
  unique_days <- filtered_data %>%
    group_by(id) %>%
    summarise(unique_days = n_distinct(as.Date(created_at)))
  
  # Count of tweets for each id
  number_tweets_boolean <- filtered_data %>%
    group_by(id) %>%
    summarise(count = n())
  
  # Naming the column respecting the time period
  year <- ifelse(academic_year_filter, "academic_time", "vacation_time")
  column_name <- paste0("avg_tweets_in_", year)
  
  # Combination of data and calculation of average posts per day
  combined_data <- left_join(unique_days, number_tweets_boolean, by = "id")
  combined_data <- combined_data %>%
    mutate(!!column_name := count / unique_days)
  
  print(combined_data)
  return(combined_data)
}
data_tweets_academic <- analyze_tweets()
data_tweets_vacations <- analyze_tweets(academic_year_filter = FALSE)

Plot for the average number of tweets during academic time for each HEI

barplot(data_tweets_academic$avg_tweets_in_academic_time,
        names.arg = data_tweets_academic$id,
        main = "Average Tweets during Academic Time",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(data_tweets_academic$avg_tweets_in_academic_time) + 5),
        las = 2,
        col = "#34495E")

text(x = barplot(data_tweets_academic$avg_tweets_in_academic_time, plot = FALSE),
     y = data_tweets_academic$avg_tweets_in_academic_time,
     labels = round(data_tweets_academic$avg_tweets_in_academic_time, 2),
     pos = 3)

Plot for the average number of tweets during vacation time for each HEI

barplot(data_tweets_vacations$avg_tweets_in_vacation_time,
        names.arg = data_tweets_vacations$id,
        main = "Average Tweets during Vacation Time",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(data_tweets_vacations$avg_tweets_in_vacation_time) + 5),
        las = 2,
        col = "#D35400")

text(x = barplot(data_tweets_vacations$avg_tweets_in_vacation_time, plot = FALSE),
     y = data_tweets_vacations$avg_tweets_in_vacation_time,
     labels = round(data_tweets_vacations$avg_tweets_in_vacation_time, 2),
     pos = 3)

Data preparation for day of the week

# Creating new table that contains a new column for the day of the week
data_tweets_days <- data_tweets %>%
  mutate(day_of_week = weekdays(created_at))

# Selecting only the id, created_at, and day_of_week columns for the new table
data_tweets_days <- data_tweets_days %>%
  select(id, created_at, day_of_week)

print(data_tweets_days)
# Grouping by id and day_of_week, then counting the number of tweets
number_tweets_days <- data_tweets_days %>%
  group_by(id, day_of_week) %>%
  summarise(count = n())
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
# Grouping by id, day_of_week and day created at, then counting the number of tweets
number_tweets_per_day <- data_tweets_days %>%
  mutate(created_date = as.Date(created_at)) %>%
  group_by(id, day_of_week, created_date) %>%
  summarise(count = n())
`summarise()` has grouped output by 'id', 'day_of_week'. You can override using the `.groups` argument.
# Finding for each HEI the average count of tweets per day
average_number_tweets_per_day <- number_tweets_per_day %>%
  group_by(id, day_of_week) %>%
  summarise(average_count = mean(count))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
print(number_tweets_days)

Highest and lowest tweets

# Finding the HEI with the lowest count of tweets per day
lowest_count <- number_tweets_days %>%
  group_by(day_of_week) %>%
  slice_min(order_by = count) %>%
  select(day_of_week, id, count)

# Same but highest count of tweets per day
highest_count <- number_tweets_days %>%
  group_by(day_of_week) %>%
  slice_max(order_by = count) %>%
  select(day_of_week, id, count)

# Combine the results
high_low_HEI <- bind_rows(lowest_count, highest_count) %>%
  arrange(day_of_week)

print(high_low_HEI)

Plot for the lowest and highest count of tweets per day for each day of the week

ggplot(high_low_HEI, aes(x = day_of_week, y = count, fill = id)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = count),
            position = position_dodge(width = 0.9),
            vjust = -0.5,
            size = 3) +
  labs(title = "Lowest and Highest Count of Tweets per Day for Each Day of the Week",
       x = "Day of the Week", y = "Count") +
  scale_fill_manual(values = rainbow(length(unique(high_low_HEI$id)))) +
  theme_minimal() +
  theme(legend.title = element_blank())

Average of tweets

# Finding the HEI with lowest and highest averaged count of tweets per day
high_low_average_HEIs <- average_number_tweets_per_day %>%
  group_by(day_of_week) %>%
  filter(average_count == max(average_count) | average_count == min(average_count)) %>%
  arrange(day_of_week, ifelse(average_count == min(average_count), average_count, -average_count))

print(high_low_average_HEIs)

Plot for the highest and lowest average count of tweets per day for each day of the week

ggplot(high_low_average_HEIs, aes(x = day_of_week, y = average_count, fill = id)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = round(average_count, 2)),
            position = position_dodge(width = 0.7),
            vjust = -0.5,
            size = 3) +
  labs(title = "Highest and Lowest Average Count of Tweets per Day for Each Day of the Week",
       x = "Day of the Week", y = "Average Count") +
  scale_fill_manual(values = rainbow(length(unique(high_low_HEI$id)))) +
  theme_minimal() +
  theme(legend.title = element_blank())

Views Likes Retweets and Replys

# Table containing views, likes, retweets and replys for each media type for each HEI
types_of_tweets <- data_tweets %>%
              group_by(id, media_type) %>%
              summarise(count = n(),
                        total_views = sum(view_count, na.rm = TRUE),
                        total_likes = sum(favorite_count, na.rm = TRUE),
                        total_retweets = sum(retweet_count, na.rm = TRUE),
                        total_replys = sum(reply_count, na.rm = TRUE))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
                        
print(types_of_tweets)                        
# Grouping by HEI and calculating the total values of views, likes and replys across all media types
total_tweets_stats <- types_of_tweets %>%
  group_by(id) %>%
  summarise(total_views = sum(total_views),
            total_likes = sum(total_likes),
            total_replys = sum(total_replys))

print(total_tweets_stats)

Function for piechart creation for views, likes and replys

pie_maker <- function(target_id = "duke.csv"){
  # Filtering data for the specific HEI
  hei_data <- types_of_tweets %>%
    filter(id == target_id)
  
  # Calculate total views for each media type for the specific ID
  hei_media_views <- hei_data %>%
    group_by(media_type) %>%
    summarise(total_views = sum(total_views),
              total_likes = sum(total_likes),
              total_replys = sum(total_replys))
  
  # Calculating the percentage of views for each media type for the specific ID
  hei_media_views$percentage_view <- hei_media_views$total_views / sum(hei_media_views$total_views) * 100
  hei_media_views$percentage_like <- hei_media_views$total_likes / sum(hei_media_views$total_likes) * 100
  hei_media_views$percentage_reply <- hei_media_views$total_replys / sum(hei_media_views$total_replys) * 100
  
  # Creating the pie chart for views
  hei_pie_chart_views <- ggplot(hei_media_views, aes(x = "", y = percentage_view, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_views, "(", round(percentage_view, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#2196F3", "animated_gif" = "#E67E22", "photo" = "#8E44AD", "video" = "#138D75")) +
    labs(title = paste("Views for each media type -", target_id))
  
  # Creating the pie chart for likes
  hei_pie_chart_likes <- ggplot(hei_media_views, aes(x = "", y = percentage_view, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_views, "(", round(percentage_view, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#E91E63", "animated_gif" = "#4A148C", "photo" = "#90CAF9", "video" = "#00BFA5")) +
    labs(title = paste("Likes for each media type -", target_id))
  
  # Creating the pie chart for replys
  hei_pie_chart_replys <- ggplot(hei_media_views, aes(x = "", y = percentage_view, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_views, "(", round(percentage_view, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#666600", "animated_gif" = "#99CCCC", "photo" = "#9966CC", "video" = "#330000")) +
    labs(title = paste("Likes for each media type -", target_id))
  
  # Print the pie charts
  print(hei_pie_chart_views)
  print(hei_pie_chart_likes)
  print(hei_pie_chart_replys)
}

Plot of piecharts for each HEI

pie_maker()

pie_maker("epfl.csv")

pie_maker("goe.csv")

pie_maker("harvard.csv")

pie_maker("leicester.csv")

pie_maker("manchester.csv")

pie_maker("mit.csv")

pie_maker("sb.csv")

pie_maker("stanford.csv")

pie_maker("trinity.csv")

pie_maker("wv.csv")

pie_maker("yale.csv")

# Calculation of like_ratio and replys_ratio percentages
ratios_tweets_table <- total_tweets_stats %>%
  mutate(like_ratio = total_likes / total_views * 100,
         replys_ratio = total_replys / total_views * 100)

# Creation of new table with each HEI, like_ratio, and replys_ratio 
hei_tweets_ratios <- ratios_tweets_table %>%
  select(id, like_ratio, replys_ratio) %>%
  distinct()

print(hei_tweets_ratios)

Plot for like_ratio and replys_ratio for each HEI

ggplot(hei_tweets_ratios, aes(x = id)) +
  geom_bar(aes(y = like_ratio, fill = "Like Ratio"), stat = "identity", position = "dodge") +
  geom_bar(aes(y = replys_ratio, fill = "Replys Ratio"), stat = "identity", position = "dodge") +
  geom_text(aes(y = like_ratio, label = round(like_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#000000") +
  geom_text(aes(y = replys_ratio, label = round(replys_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#FFFFFF") +
  labs(title = "Like and Replys Ratios by HEI",
       x = "HEI",
       y = "Ratio (%)",
       fill = "Metric") +
  scale_fill_manual(values = c("Like Ratio" = "#2196F3", "Replys Ratio" = "#F44336")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

# Table with averages of views, likes, retweets and replys
types_of_tweets_per_tweet <- types_of_tweets %>%
                        group_by(id, media_type) %>%
                        summarise(avg_views = mean(total_views / count),
                                  avg_likes = mean(total_likes / count),
                                  avg_retweets = mean(total_retweets / count),
                                  avg_replys = mean(total_replys / count))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
print(types_of_tweets_per_tweet)
# Grouping by HEI and calculating the average values of views, likes and replys across all media types
total_average_stats <- types_of_tweets_per_tweet %>%
  group_by(id) %>%
  summarise(avg_views = sum(avg_views),
            avg_likes = sum(avg_likes),
            avg_replys = sum(avg_replys))

print(total_average_stats)
# Calculation of like_ratio and replys_ratio percentages
ratios_average_table <- total_average_stats %>%
  mutate(like_ratio = avg_likes / avg_views * 100,
         replys_ratio = avg_replys / avg_views * 100)

# Creation of new table with each HEI, like_ratio, and replys_ratio 
hei_average_ratios <- ratios_average_table %>%
  select(id, like_ratio, replys_ratio) %>%
  distinct()

print(hei_average_ratios)

Plot for like_ratio and replys_ratio for each HEI

ggplot(hei_average_ratios, aes(x = id)) +
  geom_bar(aes(y = like_ratio, fill = "Like Ratio"), stat = "identity", position = "dodge") +
  geom_bar(aes(y = replys_ratio, fill = "Replys Ratio"), stat = "identity", position = "dodge") +
  geom_text(aes(y = like_ratio, label = round(like_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#000000") +
  geom_text(aes(y = replys_ratio, label = round(replys_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#FFFFFF") +
  labs(title = "Like and Replys Ratios by HEI",
       x = "HEI",
       y = "Ratio (%)",
       fill = "Metric") +
  scale_fill_manual(values = c("Like Ratio" = "#330066", "Replys Ratio" = "#FF6666")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Hashtags

# Table with number of unique hashtags
unique_hashtags <- data_tweets %>%
                group_by(id) %>%
                summarise(count = n(),
                          unique_hashtags = length(unique(hashtags)))

print(unique_hashtags)

Plot for the count of unique hashtags for each HEI

barplot(unique_hashtags$unique_hashtags,
        names.arg = unique_hashtags$id,
        main = "Unique Hashtags for Each HEI",
        xlab = "HEI",
        ylab = "Count of Unique Hashtags",
        ylim = c(0, max(unique_hashtags$unique_hashtags) + 50),
        las = 2,
        col= "#16A085")

text(x = barplot(unique_hashtags$unique_hashtags, plot = FALSE),
     y = unique_hashtags$unique_hashtags,
     labels = round(unique_hashtags$unique_hashtags, 2),
     pos = 3)

Heatmaps

# Create column hour from created_at
data_tweets_days$created_hour <- as.numeric(format(data_tweets_days$created_at, "%H"))

Function to plot heatmap for various HEIs

heatmap_maker <- function(target_id = "duke.csv"){
  # Filtering data for the specific HEI
  target_data <- data_tweets_days %>%
    filter(id == target_id)
  
  # Grouping by day of the week and hour, and counting the number of tweets
  tweet_counts <- target_data %>%
    group_by(day_of_week, created_hour) %>%
    summarise(num_tweets = n())
  
  # Plotting heatmap
  ggplot(tweet_counts, aes(x = day_of_week, y = created_hour, fill = num_tweets)) +
    geom_tile() +
    scale_fill_gradient(low = "white", high = "blue") +
    labs(title = paste("Tweet Heatmap for", target_id),
         x = "Day of the week",
         y = "Hour of the day")
}

heatmap_maker()
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("epfl.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("goe.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("harvard.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("leicester.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("manchester.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("mit.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("sb.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("stanford.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("trinity.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("wv.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("yale.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

Text

data_tweets_content <- data_tweets %>%
            select(id, text)

# Counting number of words
data_tweets_content <- data_tweets_content %>%
  mutate(num_words = lengths(strsplit(text, "\\s+")))

print(data_tweets_content)

# Grouping by HEI and calculate average, minimum, and maximum values of number of words
data_tweets_content_metrics <- data_tweets_content %>%
  group_by(id) %>%
  summarise(average_num_words = mean(num_words),
            min_num_words = min(num_words),
            max_num_words = max(num_words))
print(data_tweets_content_metrics)

Plot for the average, maximum and minimum values of words for each HEI

ggplot(data_tweets_content_metrics, aes(x = id, y = average_num_words)) +
  geom_point(aes(color = "Average")) +
  geom_errorbar(aes(ymin = min_num_words, ymax = max_num_words, color = "Range"), width = 0.2) +
  scale_color_manual(values = c("Average" = "#1976D2", "Range" = "#EF5350")) +
  labs(title = "Word Count Summary by HEI",
       x = "HEI",
       y = "Number of Words",
       color = "Metric") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Now comments

data_replies <- data[data$type == "Reply", ]

data_replies

Interactions to replys

# Table containing views, likes, retweets and replys for each media type for each HEI
types_of_replies <- data_replies %>%
              group_by(id, media_type) %>%
              summarise(count = n(),
                        total_views = sum(view_count, na.rm = TRUE),
                        total_likes = sum(favorite_count, na.rm = TRUE),
                        total_retweets = sum(retweet_count, na.rm = TRUE),
                        total_replys = sum(reply_count, na.rm = TRUE))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
                        
print(types_of_replies)                        
# Grouping by HEI and calculating the total values of views, likes and replys across all media types
total_replies_stats <- types_of_replies %>%
  group_by(id) %>%
  summarise(total_views = sum(total_views),
            total_likes = sum(total_likes),
            total_replys = sum(total_replys))

print(total_replies_stats)
# Calculation of like_ratio and replys_ratio percentages
ratios_replies_table <- total_replies_stats %>%
  mutate(like_ratio = total_likes / total_views * 100,
         replys_ratio = total_replys / total_views * 100)

# Creation of new table with each HEI, like_ratio, and replys_ratio 
hei_replies_ratios <- ratios_replies_table %>%
  select(id, like_ratio, replys_ratio) %>%
  distinct()

print(hei_replies_ratios)

Clusters

# Creating table for cluster algorithms

# Joining attribute count (number of tweets) and unique_hashtags (number of unique hashtags) per HEI
cluster_table <- merge(select(unique_hashtags, id, unique_hashtags), select(number_tweets, id, count), by = "id", all=TRUE)

# Joining attribute avg_tweets_per_days (average of tweets per day) per HEI
cluster_table <- merge(cluster_table, select(tweets_per_day, id, avg_tweets_per_days), by = "id", all=TRUE)

# Joining attribute avg_tweets_per_weeks (average of tweets per week) per HEI
cluster_table <- merge(cluster_table, select(tweets_per_week, id, avg_tweets_per_weeks), by = "id", all=TRUE)

# Joining attribute avg_tweets_in_academic_time (average of tweets during academic time) per HEI
cluster_table <- merge(cluster_table, select(data_tweets_academic, id, avg_tweets_in_academic_time), by = "id", all=TRUE)

# Joining attribute avg_tweets_in_vacation_time (average of tweets during vacation time) per HEI
cluster_table <- merge(cluster_table, select(data_tweets_vacations, id, avg_tweets_in_vacation_time), by = "id", all=TRUE)

# Joining attribute total_views (total number of views), total_likes (total number of likes) and total_replys (total number of replies) per HEI
cluster_table <- merge(cluster_table, select(total_tweets_stats, id, total_views, total_likes, total_replys), by = "id", all=TRUE)

# Joining attribute like_ratio (ratio of total number of likes) and replys_ratio (ratio of total number of replies) per HEI
cluster_table <- merge(cluster_table, select(hei_tweets_ratios, id, like_ratio, replys_ratio), by = "id", all=TRUE)
cluster_table <- cluster_table %>%
  rename(total_like_ratio = like_ratio, 
         total_replys_ratio = replys_ratio)

# Joining attribute avg_views (average number of views), avg_likes (average number of likes) and avg_replys (average number of replies) per HEI
cluster_table <- merge(cluster_table, select(total_average_stats, id, avg_views, avg_likes, avg_replys), by = "id", all=TRUE)

# Joining attribute like_ratio (ratio of average number of likes) and replys_ratio (ratio of average number of replies) per HEI
cluster_table <- merge(cluster_table, select(hei_average_ratios, id, like_ratio, replys_ratio), by = "id", all=TRUE)
cluster_table <- cluster_table %>%
  rename(avg_like_ratio = like_ratio, 
         avg_replys_ratio = replys_ratio)

print(cluster_table)

Function for cluster method

cluster_maker <- function(seed = 123, num_clusters = 3, table){
  set.seed(123)
  
  # Excluding id column for clustering
  cluster_data <- select(table, -id)
  
  # Scaling the data for kmeans method
  scaled_data <- scale(cluster_data)
  
  kmeans_result <- kmeans(scaled_data, centers = num_clusters)
  
  print(kmeans_result$centers)
  print(kmeans_result$cluster)
  
  return(kmeans_result)
}

Function to add ids to better visualize results

cluster_id_maker <- function(kmeans_result, table){
  # Merging the cluster assignments with the original data
  cluster_assignments <- data.frame(id = table$id, cluster = kmeans_result$cluster)

  print(cluster_assignments)
  plot(kmeans_result$cluster)
}

Three clusters with seed 123

cluster_123_3 <- cluster_maker(table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.6381349 -0.5376116          -0.5344623           -0.5344623                  -0.5093965                  -0.5929981  -0.3102091  -0.1331381
2      -0.3232547  1.3319287           1.3324807            1.3324807                   1.2897365                   1.3561417   1.2841600   1.2437001
3       0.4806948 -0.3971585          -0.3990092           -0.3990092                  -0.3901700                  -0.3815718  -0.4869754  -0.5552810
  total_replys total_like_ratio total_replys_ratio   avg_views  avg_likes avg_replys avg_like_ratio avg_replys_ratio
1  -0.03547406        1.3168443         1.14394012 -0.04762103  0.5778107  0.5798181      1.4433344       1.09974566
2   1.11229258       -0.2089453         0.07129591  1.21230324  0.7827831  0.6292289     -0.3283523      -0.01971601
3  -0.53840926       -0.5539495        -0.60761801 -0.58234111 -0.6802969 -0.6045235     -0.5574910      -0.54001482
 [1] 1 3 3 2 3 3 2 3 1 3 1 2
cluster_id_maker(cluster_123_3, table = cluster_table)

Six clusters with seed 123

cluster_123_6 <- cluster_maker(num_clusters = 6, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.4103492 -1.3031254          -1.3032034           -1.3032034                  -1.2416625                  -1.0231249  -0.6375984 -0.66179186
2      -0.3366538  2.0980363           2.1001655            2.1001655                   2.2152351                   2.2141794   2.7698115  2.76526795
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.2999826 -0.35327890
4      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.2090329  0.07640829
5      -0.2808240 -0.5708690          -0.5746071           -0.5746071                  -0.5989595                  -0.6377948  -0.5055879 -0.58466068
6      -0.6269689 -0.1034839          -0.1067369           -0.1067369                  -0.1791121                  -0.2061612   0.1645105  0.21131146
  total_replys total_like_ratio total_replys_ratio  avg_views  avg_likes  avg_replys avg_like_ratio avg_replys_ratio
1  -0.61859306        0.9061875        -0.73455883 -0.6997429 -0.7372601 -0.68389426     -0.2923006      -0.90543423
2   2.91587745       -0.1894786         0.24320057  2.1085449  1.4654190  1.59191180     -0.3555917       0.18423794
3  -0.31932304       -0.5257982        -0.10316261 -0.5364867 -0.6301142 -0.54350287     -0.5247867      -0.25353926
4   0.55340665        1.7400189         2.73035156  0.4273143  1.9304181  2.31424168      2.6027601       2.94250000
5  -0.55967675       -0.9230136        -0.72436706 -0.5414191 -0.6611809 -0.58166179     -0.5983170      -0.51410929
6  -0.07123056        0.6299026         0.08119858  0.4658671  0.4051027  0.05107825      0.4714812       0.02721398
 [1] 6 5 1 2 3 5 6 5 4 3 6 3
cluster_id_maker(cluster_123_6, table = cluster_table)

Four clusters with seed 4855

cluster_123_3 <- cluster_maker(seed = 4855, num_clusters = 4, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.3132053 -0.7539331          -0.7567562           -0.7567562                  -0.7596352                  -0.7341273  -0.5385905  -0.6039435
2      -0.4672956  1.2357198           1.2354314            1.2354314                   1.1911790                   1.2741657   1.9924687   1.9375124
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.2999826  -0.3532789
4      -0.6381349 -0.5376116          -0.5344623           -0.5344623                  -0.5093965                  -0.5929981  -0.3102091  -0.1331381
  total_replys total_like_ratio total_replys_ratio   avg_views  avg_likes avg_replys avg_like_ratio avg_replys_ratio
1  -0.57440583       -0.4657134         -0.7269150 -0.58100004 -0.6802007 -0.6072199     -0.5218129      -0.61194052
2   1.68100730       -0.2551425         -0.1073362  2.03816171  1.4388566  1.1599670     -0.3341956      -0.04542855
3  -0.31932304       -0.5257982         -0.1031626 -0.53648673 -0.6301142 -0.5435029     -0.5247867      -0.25353926
4  -0.03547406        1.3168443          1.1439401 -0.04762103  0.5778107  0.5798181      1.4433344       1.09974566
 [1] 4 1 1 2 3 1 2 1 4 3 4 3
cluster_id_maker(cluster_123_3, table = cluster_table)

Six clusters with seed 4855

cluster_123_6 <- cluster_maker(seed = 4855, num_clusters = 6, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.4103492 -1.3031254          -1.3032034           -1.3032034                  -1.2416625                  -1.0231249  -0.6375984 -0.66179186
2      -0.3366538  2.0980363           2.1001655            2.1001655                   2.2152351                   2.2141794   2.7698115  2.76526795
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.2999826 -0.35327890
4      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.2090329  0.07640829
5      -0.2808240 -0.5708690          -0.5746071           -0.5746071                  -0.5989595                  -0.6377948  -0.5055879 -0.58466068
6      -0.6269689 -0.1034839          -0.1067369           -0.1067369                  -0.1791121                  -0.2061612   0.1645105  0.21131146
  total_replys total_like_ratio total_replys_ratio  avg_views  avg_likes  avg_replys avg_like_ratio avg_replys_ratio
1  -0.61859306        0.9061875        -0.73455883 -0.6997429 -0.7372601 -0.68389426     -0.2923006      -0.90543423
2   2.91587745       -0.1894786         0.24320057  2.1085449  1.4654190  1.59191180     -0.3555917       0.18423794
3  -0.31932304       -0.5257982        -0.10316261 -0.5364867 -0.6301142 -0.54350287     -0.5247867      -0.25353926
4   0.55340665        1.7400189         2.73035156  0.4273143  1.9304181  2.31424168      2.6027601       2.94250000
5  -0.55967675       -0.9230136        -0.72436706 -0.5414191 -0.6611809 -0.58166179     -0.5983170      -0.51410929
6  -0.07123056        0.6299026         0.08119858  0.4658671  0.4051027  0.05107825      0.4714812       0.02721398
 [1] 6 5 1 2 3 5 6 5 4 3 6 3
cluster_id_maker(cluster_123_6, table = cluster_table)

LS0tCnRpdGxlOiAiUHJvamVjdCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIFRPRE8gdHJhdGFyIGRlIE5BcyBlIHBhc3NhciB0YWJlbGFzIHByYSBjc3YKCiMjIyBGb3IgUiBiZWdpbm5lcnMKTmV3IGNodW5rICpDdHJsK0FsdCtJKgoKRXhlY3V0ZSBjaHVuayAqQ3RybCtTaGlmdCtFbnRlcioKCkV4ZWN1dGUgYWxsIGNodW5rcyAqQ3RybCtBbHQrUioKCkhUTUwgcHJldmlldyAqQ3RybCtTaGlmdCtLKgoKIyBMaWJyYXJ5IHByZXBhcmF0aW9ucwoKYGBge3J9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoc3RhdHMpCmBgYAoKIyBEYXRhIEltcG9ydAoKYGBge3J9CmRhdGEgPC0gcmVhZC5jc3YoIn4vNHllYXIvMnNlbWVzdGVyL2R0SUkvQ1NWcy9IRUlzLmNzdiIsCiAgICAgICAgICAgICAgICAgY29sQ2xhc3NlcyA9IGModHdlZXRfaWQgPSAiY2hhcmFjdGVyIikpCgojIE1vZGlmeWluZyBjcmVhdGVkX2F0IHR5cGUgc28gdGhhdCBhdHRyaWJ1dGUgY2FuIGJlIHVzZWQgbW9yZSBlYXNpbHkgCmRhdGEkY3JlYXRlZF9hdCA8LSBhcy5QT1NJWGN0KGRhdGEkY3JlYXRlZF9hdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0PSAiJVktJW0tJWRUJUg6JU06JVMiLCB0ej0iVVRDIikKCiNWaWV3KGRhdGEpCnN1bW1hcnkoZGF0YSkKYGBgCgojIEluaXRpYWwgRGF0YSBQcmVwYXJhdGlvbgoKYGBge3J9CiMgVmlldyBvZiBob3cgbWFueSBlbnRyaWVzIGVhY2ggSEVJIGhhcwpudW1iZXJfaW50ZXJhY3Rpb25zIDwtIGRhdGEgJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpudW1iZXJfaW50ZXJhY3Rpb25zCmBgYAoKYGBge3J9CiMgU2luY2UgY29tcGx1dGVuc2Ugb25seSBoYXMgMSBlbnRyeSB3ZSBjYW4ndCBsZWFybiBhbnl0aGluZyBmcm9tIGl0LCBzbyB3ZSByZW1vdmVkIGl0CmRhdGEgPC0gZGF0YVtkYXRhJGlkICE9ICJjb21wbHV0ZW5zZS5jc3YiLCBdCmBgYAoKYGBge3J9CiMgVmlzdWFsaXphdGlvbiBvZiBudW1iZXIgYWxsIHBvc3RzLCBqdXN0IHR3ZWV0cyBhbmQganVzdCByZXBseXMKbnVtYmVyX3Bvc3RzIDwtIGRhdGEgJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpudW1iZXJfdHdlZXRzIDwtIGRhdGFbZGF0YSR0eXBlID09ICJUd2VldCIsIF0gJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpudW1iZXJfcmVwbHlzIDwtIGRhdGFbZGF0YSR0eXBlID09ICJSZXBseSIsIF0gJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpwcmludChudW1iZXJfcG9zdHMpCnByaW50KG51bWJlcl90d2VldHMpCnByaW50KG51bWJlcl9yZXBseXMpCmBgYAoKIyBDYWxjdWxhdGluZyB0aGUgcGVyY2VudGFnZSBvZiB0d2VldHMgYW5kIHJlcGxpZXMgYmFzZWQgb24gYWxsIHBvc3RzCgpgYGB7cn0KIyBNZXJnaW5nIHRoZSBjb3VudHMgb2YgdHdlZXRzIChjb3VudC55KSBhbmQgcmVwbGllcyAoY291bnQpIHdpdGggdGhlIGNvdW50IG9mIHBvc3RzIChjb3VudC54KQpkYXRhX3JhdGlvIDwtIG1lcmdlKG51bWJlcl9wb3N0cywgbnVtYmVyX3R3ZWV0cywgYnkgPSAiaWQiLCBhbGwgPSBUUlVFKQpkYXRhX3JhdGlvIDwtIG1lcmdlKGRhdGFfcmF0aW8sIG51bWJlcl9yZXBseXMsIGJ5ID0gImlkIiwgYWxsID0gVFJVRSkKCgpkYXRhX3JhdGlvJHBlcmNlbnRhZ2VfdHdlZXRzIDwtIChkYXRhX3JhdGlvJGNvdW50LnkgLyBkYXRhX3JhdGlvJGNvdW50LngpICogMTAwCmRhdGFfcmF0aW8kcGVyY2VudGFnZV9yZXBsaWVzIDwtIChkYXRhX3JhdGlvJGNvdW50IC8gZGF0YV9yYXRpbyRjb3VudC54KSAqIDEwMAoKZGF0YV9yYXRpbyA8LSBkYXRhX3JhdGlvWywgYygiaWQiLCAicGVyY2VudGFnZV90d2VldHMiLCAicGVyY2VudGFnZV9yZXBsaWVzIildCgpwcmludChkYXRhX3JhdGlvKQpgYGAKCiMgRm9yIG5vdyB3ZSdsbCBiZSBvbmx5IGxvb2tpbmcgYXQgdHdlZXRzCgpgYGB7cn0KZGF0YV90d2VldHMgPC0gZGF0YVtkYXRhJHR5cGUgPT0gIlR3ZWV0IiwgXQoKZGF0YV90d2VldHMKYGBgCgoKIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgYXZlcmFnZSBwb3N0cwoKYGBge3J9CmF2ZXJhZ2VfdHdlZXRzIDwtIGZ1bmN0aW9uKHRpbWVmcmFtZSA9ICJkYXlzIil7CiAgIyBDYWxjdWxhdGlvbiBvZiB0aGUgdGltZWZyYW1lIGJldHdlZW4gZWFybGllc3QgYW5kIGxhdGVzdCBwb3N0IGZvciBlYWNoIEhFSQogIGRhdGVfcmFuZ2UgPC0gZGF0YV90d2VldHMgJT4lCiAgICBncm91cF9ieShpZCkgJT4lCiAgICBzdW1tYXJpc2UobWluX2RhdGUgPSBtaW4oY3JlYXRlZF9hdCksCiAgICAgICAgICAgICAgbWF4X2RhdGUgPSBtYXgoY3JlYXRlZF9hdCkpICU+JQogICAgbXV0YXRlKG51bV9kYXlzID0gYXMubnVtZXJpYyhkaWZmdGltZShtYXhfZGF0ZSwgbWluX2RhdGUsIHVuaXRzID0gdGltZWZyYW1lKSkpCiAgCiAgIyBOYW1pbmcgdGhlIGNvbHVtbiByZXNwZWN0aW5nIHRoZSB0aW1lZnJhbWUKICBjb2x1bW5fbmFtZSA8LSBwYXN0ZTAoImF2Z190d2VldHNfcGVyXyIsIHRpbWVmcmFtZSkKICAKICAjIENhbGN1bGF0aW9uIG9mIHRoZSBudW1iZXIgb2YgdHdlZXRzIHBlciBkYXkgZm9yIGVhY2ggSEVJCiAgdHdlZXRzX3Blcl90aW1lZnJhbWUgPC0gbnVtYmVyX3R3ZWV0cyAlPiUKICAgIGxlZnRfam9pbihkYXRlX3JhbmdlLCBieSA9ICJpZCIpICU+JQogICAgbXV0YXRlKCEhY29sdW1uX25hbWUgOj0gY291bnQgLyBudW1fZGF5cykKICAKICBwcmludCh0d2VldHNfcGVyX3RpbWVmcmFtZSkKICByZXR1cm4odHdlZXRzX3Blcl90aW1lZnJhbWUpCn0KYGBgCgpgYGB7cn0KdHdlZXRzX3Blcl9kYXkgPC0gYXZlcmFnZV90d2VldHMoKQp0d2VldHNfcGVyX3dlZWsgPC0gYXZlcmFnZV90d2VldHModGltZWZyYW1lID0gIndlZWtzIikKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0d2VldHMgcGVyIGRheSBmb3IgZWFjaCBIRUkKCmBgYHtyfQpiYXJwbG90KHR3ZWV0c19wZXJfZGF5JGF2Z190d2VldHNfcGVyX2RheXMsCiAgICAgICAgbmFtZXMuYXJnID0gdHdlZXRzX3Blcl9kYXkkaWQsCiAgICAgICAgbWFpbiA9ICJBdmVyYWdlIFR3ZWV0cyBwZXIgRGF5IiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJBdmVyYWdlIE51bWJlciBvZiBUd2VldHMiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heCh0d2VldHNfcGVyX2RheSRhdmdfdHdlZXRzX3Blcl9kYXlzKSArIDEpLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sID0gIiMzNDk4REIiKQoKIyBBZGRpbmcgdGV4dCBsYWJlbHMgb3ZlciBlYWNoIGJhciBhbmQgYWxpZ25pbmcgaXQgd2l0aCB0aGUgY2VudGVyIG9mIGVhY2ggYmFyIAp0ZXh0KHggPSBiYXJwbG90KHR3ZWV0c19wZXJfZGF5JGF2Z190d2VldHNfcGVyX2RheXMsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IHR3ZWV0c19wZXJfZGF5JGF2Z190d2VldHNfcGVyX2RheXMsCiAgICAgbGFiZWxzID0gcm91bmQodHdlZXRzX3Blcl9kYXkkYXZnX3R3ZWV0c19wZXJfZGF5cywgMiksCiAgICAgcG9zID0gMykKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0d2VldHMgcGVyIHdlZWsgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdCh0d2VldHNfcGVyX3dlZWskYXZnX3R3ZWV0c19wZXJfd2Vla3MsCiAgICAgICAgbmFtZXMuYXJnID0gdHdlZXRzX3Blcl93ZWVrJGlkLAogICAgICAgIG1haW4gPSAiQXZlcmFnZSBUd2VldHMgcGVyIFdlZWsiLAogICAgICAgIHhsYWIgPSAiSEVJIiwKICAgICAgICB5bGFiID0gIkF2ZXJhZ2UgTnVtYmVyIG9mIFR3ZWV0cyIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHR3ZWV0c19wZXJfd2VlayRhdmdfdHdlZXRzX3Blcl93ZWVrcykgKyA1KSwKICAgICAgICBsYXMgPSAyLAogICAgICAgIGNvbCA9ICIjRTc0QzNDIikKCnRleHQoeCA9IGJhcnBsb3QodHdlZXRzX3Blcl93ZWVrJGF2Z190d2VldHNfcGVyX3dlZWtzLCBwbG90ID0gRkFMU0UpLAogICAgIHkgPSB0d2VldHNfcGVyX3dlZWskYXZnX3R3ZWV0c19wZXJfd2Vla3MsCiAgICAgbGFiZWxzID0gcm91bmQodHdlZXRzX3Blcl93ZWVrJGF2Z190d2VldHNfcGVyX3dlZWtzLCAyKSwKICAgICBwb3MgPSAzKQpgYGAKCiMgRGVmaW5pbmcgdGhlIGludGVydmFscyBvZiB0aW1lIGZvciB0aGUgYWNhZGVtaWMgeWVhcgoKYGBge3J9CmludGVydmFscyA8LSBsaXN0KAogIGludGVydmFsMSA9IGFzLlBPU0lYY3QoYygiMjAyMi0wOC0zMSIsICIyMDIyLTEyLTE1IikpLAogIGludGVydmFsMiA9IGFzLlBPU0lYY3QoYygiMjAyMy0wMS0wNCIsICIyMDIzLTA0LTAxIikpLAogIGludGVydmFsMyA9IGFzLlBPU0lYY3QoYygiMjAyMy0wNC0xNCIsICIyMDIzLTA2LTE1IikpCikKYGBgCgojIEZ1bmN0aW9uIHRvIGNoZWNrIGlmIGEgZGF0ZSBmYWxscyB3aXRoaW4gYSBnaXZlbiBpbnRlcnZhbCBvZiB0aW1lIGFuZCBhcHBseSBhcHByb3ByaWF0ZSBCb29sZWFuCgpgYGB7cn0KY2hlY2tfaW50ZXJ2YWwgPC0gZnVuY3Rpb24oZGF0ZSkgewogIGZvciAoaSBpbiAxOmxlbmd0aChpbnRlcnZhbHMpKSB7CiAgICBpbnRlcnZhbF9zdGFydCA8LSBpbnRlcnZhbHNbW2ldXVsxXQogICAgaW50ZXJ2YWxfZW5kIDwtIGludGVydmFsc1tbaV1dWzJdCiAgICBpZiAoZGF0ZSA+PSBpbnRlcnZhbF9zdGFydCAmIGRhdGUgPD0gaW50ZXJ2YWxfZW5kKSB7CiAgICAgIHJldHVybihUUlVFKQogICAgfQogIH0KICByZXR1cm4oRkFMU0UpCn0KYGBgCgpgYGB7cn0KZGF0YV90d2VldHMkYWNhZGVtaWNfeWVhciA8LSBzYXBwbHkoZGF0YV90d2VldHMkY3JlYXRlZF9hdCwgY2hlY2tfaW50ZXJ2YWwpCnByaW50KGRhdGEuZnJhbWUoaWQgPSBkYXRhX3R3ZWV0cyRpZCwgYWNhZGVtaWNfeWVhciA9IGRhdGFfdHdlZXRzJGFjYWRlbWljX3llYXIpKQpgYGAKCiMgUGxvdCBmb3IgdGhlIG51bWJlciBvZiB0d2VldHMgcGVyIHRpbWVmcmFtZSBvZiBlaXRoZXIgdmFjYXRpb24gb3IgYWNhZGVtaWMgdGltZQoKYGBge3J9CmJhcnBsb3QodGFibGUoZGF0YV90d2VldHMkYWNhZGVtaWNfeWVhciksCiAgICAgICAgbWFpbiA9ICJOdW1iZXIgb2YgVHdlZXRzIHBlciBUaW1lZnJhbWUiLAogICAgICAgIHhsYWIgPSAiVGltZSIsCiAgICAgICAgeWxhYiA9ICJDb3VudCIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHRhYmxlKGRhdGFfdHdlZXRzJGFjYWRlbWljX3llYXIpKSArIDEwMDApLAogICAgICAgIG5hbWVzLmFyZyA9IGMoIlZhY2F0aW9uIiwgIkFjYWRlbWljIiksCiAgICAgICAgY29sID0gYygiIzhFNDRBRCIsICIjRjFDNDBGIikpCgp0ZXh0KHggPSBiYXJwbG90KGRhdGFfdHdlZXRzJGFjYWRlbWljX3llYXIsIHBsb3QgPSBGQUxTRSksIAogICAgIHkgPSB0YWJsZShkYXRhX3R3ZWV0cyRhY2FkZW1pY195ZWFyKSArIDAuNSwgCiAgICAgbGFiZWxzID0gdGFibGUoZGF0YV90d2VldHMkYWNhZGVtaWNfeWVhciksIAogICAgIHBvcyA9IDMpCmBgYAoKIyBGdW5jdGlvbiB0byBjb3VudCBudW1iZXIgb2YgdHdlZXRzIGFuZCBhdmVyYWdlIHBlciBkYXkKCmBgYHtyfQphbmFseXplX3R3ZWV0cyA8LSBmdW5jdGlvbihhY2FkZW1pY195ZWFyX2ZpbHRlciA9IFRSVUUpIHsKICAjIEZpbHRlcmluZyB0aGUgZGF0YSBiYXNlZCBvbiB0aGUgYWNhZGVtaWNfeWVhcl9maWx0ZXIKICBmaWx0ZXJlZF9kYXRhIDwtIGRhdGFfdHdlZXRzICU+JQogICAgZmlsdGVyKGFjYWRlbWljX3llYXIgPT0gYWNhZGVtaWNfeWVhcl9maWx0ZXIpCiAgCiAgIyBDb3VudCBvZiBkYXlzIGZvciBlYWNoIEhFSQogIHVuaXF1ZV9kYXlzIDwtIGZpbHRlcmVkX2RhdGEgJT4lCiAgICBncm91cF9ieShpZCkgJT4lCiAgICBzdW1tYXJpc2UodW5pcXVlX2RheXMgPSBuX2Rpc3RpbmN0KGFzLkRhdGUoY3JlYXRlZF9hdCkpKQogIAogICMgQ291bnQgb2YgdHdlZXRzIGZvciBlYWNoIGlkCiAgbnVtYmVyX3R3ZWV0c19ib29sZWFuIDwtIGZpbHRlcmVkX2RhdGEgJT4lCiAgICBncm91cF9ieShpZCkgJT4lCiAgICBzdW1tYXJpc2UoY291bnQgPSBuKCkpCiAgCiAgIyBOYW1pbmcgdGhlIGNvbHVtbiByZXNwZWN0aW5nIHRoZSB0aW1lIHBlcmlvZAogIHllYXIgPC0gaWZlbHNlKGFjYWRlbWljX3llYXJfZmlsdGVyLCAiYWNhZGVtaWNfdGltZSIsICJ2YWNhdGlvbl90aW1lIikKICBjb2x1bW5fbmFtZSA8LSBwYXN0ZTAoImF2Z190d2VldHNfaW5fIiwgeWVhcikKICAKICAjIENvbWJpbmF0aW9uIG9mIGRhdGEgYW5kIGNhbGN1bGF0aW9uIG9mIGF2ZXJhZ2UgcG9zdHMgcGVyIGRheQogIGNvbWJpbmVkX2RhdGEgPC0gbGVmdF9qb2luKHVuaXF1ZV9kYXlzLCBudW1iZXJfdHdlZXRzX2Jvb2xlYW4sIGJ5ID0gImlkIikKICBjb21iaW5lZF9kYXRhIDwtIGNvbWJpbmVkX2RhdGEgJT4lCiAgICBtdXRhdGUoISFjb2x1bW5fbmFtZSA6PSBjb3VudCAvIHVuaXF1ZV9kYXlzKQogIAogIHByaW50KGNvbWJpbmVkX2RhdGEpCiAgcmV0dXJuKGNvbWJpbmVkX2RhdGEpCn0KYGBgCgpgYGB7cn0KZGF0YV90d2VldHNfYWNhZGVtaWMgPC0gYW5hbHl6ZV90d2VldHMoKQpkYXRhX3R3ZWV0c192YWNhdGlvbnMgPC0gYW5hbHl6ZV90d2VldHMoYWNhZGVtaWNfeWVhcl9maWx0ZXIgPSBGQUxTRSkKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0d2VldHMgZHVyaW5nIGFjYWRlbWljIHRpbWUgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdChkYXRhX3R3ZWV0c19hY2FkZW1pYyRhdmdfdHdlZXRzX2luX2FjYWRlbWljX3RpbWUsCiAgICAgICAgbmFtZXMuYXJnID0gZGF0YV90d2VldHNfYWNhZGVtaWMkaWQsCiAgICAgICAgbWFpbiA9ICJBdmVyYWdlIFR3ZWV0cyBkdXJpbmcgQWNhZGVtaWMgVGltZSIsCiAgICAgICAgeGxhYiA9ICJIRUkiLAogICAgICAgIHlsYWIgPSAiQXZlcmFnZSBOdW1iZXIgb2YgVHdlZXRzIiwKICAgICAgICB5bGltID0gYygwLCBtYXgoZGF0YV90d2VldHNfYWNhZGVtaWMkYXZnX3R3ZWV0c19pbl9hY2FkZW1pY190aW1lKSArIDUpLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sID0gIiMzNDQ5NUUiKQoKdGV4dCh4ID0gYmFycGxvdChkYXRhX3R3ZWV0c19hY2FkZW1pYyRhdmdfdHdlZXRzX2luX2FjYWRlbWljX3RpbWUsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IGRhdGFfdHdlZXRzX2FjYWRlbWljJGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSwKICAgICBsYWJlbHMgPSByb3VuZChkYXRhX3R3ZWV0c19hY2FkZW1pYyRhdmdfdHdlZXRzX2luX2FjYWRlbWljX3RpbWUsIDIpLAogICAgIHBvcyA9IDMpCmBgYAoKIyBQbG90IGZvciB0aGUgYXZlcmFnZSBudW1iZXIgb2YgdHdlZXRzIGR1cmluZyB2YWNhdGlvbiB0aW1lIGZvciBlYWNoIEhFSQoKYGBge3J9CmJhcnBsb3QoZGF0YV90d2VldHNfdmFjYXRpb25zJGF2Z190d2VldHNfaW5fdmFjYXRpb25fdGltZSwKICAgICAgICBuYW1lcy5hcmcgPSBkYXRhX3R3ZWV0c192YWNhdGlvbnMkaWQsCiAgICAgICAgbWFpbiA9ICJBdmVyYWdlIFR3ZWV0cyBkdXJpbmcgVmFjYXRpb24gVGltZSIsCiAgICAgICAgeGxhYiA9ICJIRUkiLAogICAgICAgIHlsYWIgPSAiQXZlcmFnZSBOdW1iZXIgb2YgVHdlZXRzIiwKICAgICAgICB5bGltID0gYygwLCBtYXgoZGF0YV90d2VldHNfdmFjYXRpb25zJGF2Z190d2VldHNfaW5fdmFjYXRpb25fdGltZSkgKyA1KSwKICAgICAgICBsYXMgPSAyLAogICAgICAgIGNvbCA9ICIjRDM1NDAwIikKCnRleHQoeCA9IGJhcnBsb3QoZGF0YV90d2VldHNfdmFjYXRpb25zJGF2Z190d2VldHNfaW5fdmFjYXRpb25fdGltZSwgcGxvdCA9IEZBTFNFKSwKICAgICB5ID0gZGF0YV90d2VldHNfdmFjYXRpb25zJGF2Z190d2VldHNfaW5fdmFjYXRpb25fdGltZSwKICAgICBsYWJlbHMgPSByb3VuZChkYXRhX3R3ZWV0c192YWNhdGlvbnMkYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lLCAyKSwKICAgICBwb3MgPSAzKQpgYGAKCiMgRGF0YSBwcmVwYXJhdGlvbiBmb3IgZGF5IG9mIHRoZSB3ZWVrIAoKYGBge3J9CiMgQ3JlYXRpbmcgbmV3IHRhYmxlIHRoYXQgY29udGFpbnMgYSBuZXcgY29sdW1uIGZvciB0aGUgZGF5IG9mIHRoZSB3ZWVrCmRhdGFfdHdlZXRzX2RheXMgPC0gZGF0YV90d2VldHMgJT4lCiAgbXV0YXRlKGRheV9vZl93ZWVrID0gd2Vla2RheXMoY3JlYXRlZF9hdCkpCgojIFNlbGVjdGluZyBvbmx5IHRoZSBpZCwgY3JlYXRlZF9hdCwgYW5kIGRheV9vZl93ZWVrIGNvbHVtbnMgZm9yIHRoZSBuZXcgdGFibGUKZGF0YV90d2VldHNfZGF5cyA8LSBkYXRhX3R3ZWV0c19kYXlzICU+JQogIHNlbGVjdChpZCwgY3JlYXRlZF9hdCwgZGF5X29mX3dlZWspCgpwcmludChkYXRhX3R3ZWV0c19kYXlzKQpgYGAKCmBgYHtyfQojIEdyb3VwaW5nIGJ5IGlkIGFuZCBkYXlfb2Zfd2VlaywgdGhlbiBjb3VudGluZyB0aGUgbnVtYmVyIG9mIHR3ZWV0cwpudW1iZXJfdHdlZXRzX2RheXMgPC0gZGF0YV90d2VldHNfZGF5cyAlPiUKICBncm91cF9ieShpZCwgZGF5X29mX3dlZWspICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKCiMgR3JvdXBpbmcgYnkgaWQsIGRheV9vZl93ZWVrIGFuZCBkYXkgY3JlYXRlZCBhdCwgdGhlbiBjb3VudGluZyB0aGUgbnVtYmVyIG9mIHR3ZWV0cwpudW1iZXJfdHdlZXRzX3Blcl9kYXkgPC0gZGF0YV90d2VldHNfZGF5cyAlPiUKICBtdXRhdGUoY3JlYXRlZF9kYXRlID0gYXMuRGF0ZShjcmVhdGVkX2F0KSkgJT4lCiAgZ3JvdXBfYnkoaWQsIGRheV9vZl93ZWVrLCBjcmVhdGVkX2RhdGUpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKCiMgRmluZGluZyBmb3IgZWFjaCBIRUkgdGhlIGF2ZXJhZ2UgY291bnQgb2YgdHdlZXRzIHBlciBkYXkKYXZlcmFnZV9udW1iZXJfdHdlZXRzX3Blcl9kYXkgPC0gbnVtYmVyX3R3ZWV0c19wZXJfZGF5ICU+JQogIGdyb3VwX2J5KGlkLCBkYXlfb2Zfd2VlaykgJT4lCiAgc3VtbWFyaXNlKGF2ZXJhZ2VfY291bnQgPSBtZWFuKGNvdW50KSkKCnByaW50KG51bWJlcl90d2VldHNfZGF5cykKYGBgCgojIEhpZ2hlc3QgYW5kIGxvd2VzdCB0d2VldHMKCmBgYHtyfQojIEZpbmRpbmcgdGhlIEhFSSB3aXRoIHRoZSBsb3dlc3QgY291bnQgb2YgdHdlZXRzIHBlciBkYXkKbG93ZXN0X2NvdW50IDwtIG51bWJlcl90d2VldHNfZGF5cyAlPiUKICBncm91cF9ieShkYXlfb2Zfd2VlaykgJT4lCiAgc2xpY2VfbWluKG9yZGVyX2J5ID0gY291bnQpICU+JQogIHNlbGVjdChkYXlfb2Zfd2VlaywgaWQsIGNvdW50KQoKIyBTYW1lIGJ1dCBoaWdoZXN0IGNvdW50IG9mIHR3ZWV0cyBwZXIgZGF5CmhpZ2hlc3RfY291bnQgPC0gbnVtYmVyX3R3ZWV0c19kYXlzICU+JQogIGdyb3VwX2J5KGRheV9vZl93ZWVrKSAlPiUKICBzbGljZV9tYXgob3JkZXJfYnkgPSBjb3VudCkgJT4lCiAgc2VsZWN0KGRheV9vZl93ZWVrLCBpZCwgY291bnQpCgojIENvbWJpbmUgdGhlIHJlc3VsdHMKaGlnaF9sb3dfSEVJIDwtIGJpbmRfcm93cyhsb3dlc3RfY291bnQsIGhpZ2hlc3RfY291bnQpICU+JQogIGFycmFuZ2UoZGF5X29mX3dlZWspCgpwcmludChoaWdoX2xvd19IRUkpCmBgYAoKIyBQbG90IGZvciB0aGUgbG93ZXN0IGFuZCBoaWdoZXN0IGNvdW50IG9mIHR3ZWV0cyBwZXIgZGF5IGZvciBlYWNoIGRheSBvZiB0aGUgd2VlawoKYGBge3J9CmdncGxvdChoaWdoX2xvd19IRUksIGFlcyh4ID0gZGF5X29mX3dlZWssIHkgPSBjb3VudCwgZmlsbCA9IGlkKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY291bnQpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41LAogICAgICAgICAgICBzaXplID0gMykgKwogIGxhYnModGl0bGUgPSAiTG93ZXN0IGFuZCBIaWdoZXN0IENvdW50IG9mIFR3ZWV0cyBwZXIgRGF5IGZvciBFYWNoIERheSBvZiB0aGUgV2VlayIsCiAgICAgICB4ID0gIkRheSBvZiB0aGUgV2VlayIsIHkgPSAiQ291bnQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcmFpbmJvdyhsZW5ndGgodW5pcXVlKGhpZ2hfbG93X0hFSSRpZCkpKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMgQXZlcmFnZSBvZiB0d2VldHMKCmBgYHtyfQojIEZpbmRpbmcgdGhlIEhFSSB3aXRoIGxvd2VzdCBhbmQgaGlnaGVzdCBhdmVyYWdlZCBjb3VudCBvZiB0d2VldHMgcGVyIGRheQpoaWdoX2xvd19hdmVyYWdlX0hFSXMgPC0gYXZlcmFnZV9udW1iZXJfdHdlZXRzX3Blcl9kYXkgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3dlZWspICU+JQogIGZpbHRlcihhdmVyYWdlX2NvdW50ID09IG1heChhdmVyYWdlX2NvdW50KSB8IGF2ZXJhZ2VfY291bnQgPT0gbWluKGF2ZXJhZ2VfY291bnQpKSAlPiUKICBhcnJhbmdlKGRheV9vZl93ZWVrLCBpZmVsc2UoYXZlcmFnZV9jb3VudCA9PSBtaW4oYXZlcmFnZV9jb3VudCksIGF2ZXJhZ2VfY291bnQsIC1hdmVyYWdlX2NvdW50KSkKCnByaW50KGhpZ2hfbG93X2F2ZXJhZ2VfSEVJcykKYGBgCgojIFBsb3QgZm9yIHRoZSBoaWdoZXN0IGFuZCBsb3dlc3QgYXZlcmFnZSBjb3VudCBvZiB0d2VldHMgcGVyIGRheSBmb3IgZWFjaCBkYXkgb2YgdGhlIHdlZWsKCmBgYHtyfQpnZ3Bsb3QoaGlnaF9sb3dfYXZlcmFnZV9IRUlzLCBhZXMoeCA9IGRheV9vZl93ZWVrLCB5ID0gYXZlcmFnZV9jb3VudCwgZmlsbCA9IGlkKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoYXZlcmFnZV9jb3VudCwgMikpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC43KSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41LAogICAgICAgICAgICBzaXplID0gMykgKwogIGxhYnModGl0bGUgPSAiSGlnaGVzdCBhbmQgTG93ZXN0IEF2ZXJhZ2UgQ291bnQgb2YgVHdlZXRzIHBlciBEYXkgZm9yIEVhY2ggRGF5IG9mIHRoZSBXZWVrIiwKICAgICAgIHggPSAiRGF5IG9mIHRoZSBXZWVrIiwgeSA9ICJBdmVyYWdlIENvdW50IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHJhaW5ib3cobGVuZ3RoKHVuaXF1ZShoaWdoX2xvd19IRUkkaWQpKSkpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCiMgVmlld3MgTGlrZXMgUmV0d2VldHMgYW5kIFJlcGx5cwoKYGBge3J9CiMgVGFibGUgY29udGFpbmluZyB2aWV3cywgbGlrZXMsIHJldHdlZXRzIGFuZCByZXBseXMgZm9yIGVhY2ggbWVkaWEgdHlwZSBmb3IgZWFjaCBIRUkKdHlwZXNfb2ZfdHdlZXRzIDwtIGRhdGFfdHdlZXRzICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkLCBtZWRpYV90eXBlKSAlPiUKICAgICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsX3ZpZXdzID0gc3VtKHZpZXdfY291bnQsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsX2xpa2VzID0gc3VtKGZhdm9yaXRlX2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9yZXR3ZWV0cyA9IHN1bShyZXR3ZWV0X2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9yZXBseXMgPSBzdW0ocmVwbHlfY291bnQsIG5hLnJtID0gVFJVRSkpCiAgICAgICAgICAgICAgICAgICAgICAgIApwcmludCh0eXBlc19vZl90d2VldHMpICAgICAgICAgICAgICAgICAgICAgICAgCmBgYAoKYGBge3J9CiMgR3JvdXBpbmcgYnkgSEVJIGFuZCBjYWxjdWxhdGluZyB0aGUgdG90YWwgdmFsdWVzIG9mIHZpZXdzLCBsaWtlcyBhbmQgcmVwbHlzIGFjcm9zcyBhbGwgbWVkaWEgdHlwZXMKdG90YWxfdHdlZXRzX3N0YXRzIDwtIHR5cGVzX29mX3R3ZWV0cyAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKHRvdGFsX3ZpZXdzID0gc3VtKHRvdGFsX3ZpZXdzKSwKICAgICAgICAgICAgdG90YWxfbGlrZXMgPSBzdW0odG90YWxfbGlrZXMpLAogICAgICAgICAgICB0b3RhbF9yZXBseXMgPSBzdW0odG90YWxfcmVwbHlzKSkKCnByaW50KHRvdGFsX3R3ZWV0c19zdGF0cykKYGBgCgojIEZ1bmN0aW9uIGZvciBwaWVjaGFydCBjcmVhdGlvbiBmb3Igdmlld3MsIGxpa2VzIGFuZCByZXBseXMgCgpgYGB7cn0KcGllX21ha2VyIDwtIGZ1bmN0aW9uKHRhcmdldF9pZCA9ICJkdWtlLmNzdiIpewogICMgRmlsdGVyaW5nIGRhdGEgZm9yIHRoZSBzcGVjaWZpYyBIRUkKICBoZWlfZGF0YSA8LSB0eXBlc19vZl90d2VldHMgJT4lCiAgICBmaWx0ZXIoaWQgPT0gdGFyZ2V0X2lkKQogIAogICMgQ2FsY3VsYXRlIHRvdGFsIHZpZXdzIGZvciBlYWNoIG1lZGlhIHR5cGUgZm9yIHRoZSBzcGVjaWZpYyBJRAogIGhlaV9tZWRpYV92aWV3cyA8LSBoZWlfZGF0YSAlPiUKICAgIGdyb3VwX2J5KG1lZGlhX3R5cGUpICU+JQogICAgc3VtbWFyaXNlKHRvdGFsX3ZpZXdzID0gc3VtKHRvdGFsX3ZpZXdzKSwKICAgICAgICAgICAgICB0b3RhbF9saWtlcyA9IHN1bSh0b3RhbF9saWtlcyksCiAgICAgICAgICAgICAgdG90YWxfcmVwbHlzID0gc3VtKHRvdGFsX3JlcGx5cykpCiAgCiAgIyBDYWxjdWxhdGluZyB0aGUgcGVyY2VudGFnZSBvZiB2aWV3cyBmb3IgZWFjaCBtZWRpYSB0eXBlIGZvciB0aGUgc3BlY2lmaWMgSUQKICBoZWlfbWVkaWFfdmlld3MkcGVyY2VudGFnZV92aWV3IDwtIGhlaV9tZWRpYV92aWV3cyR0b3RhbF92aWV3cyAvIHN1bShoZWlfbWVkaWFfdmlld3MkdG90YWxfdmlld3MpICogMTAwCiAgaGVpX21lZGlhX3ZpZXdzJHBlcmNlbnRhZ2VfbGlrZSA8LSBoZWlfbWVkaWFfdmlld3MkdG90YWxfbGlrZXMgLyBzdW0oaGVpX21lZGlhX3ZpZXdzJHRvdGFsX2xpa2VzKSAqIDEwMAogIGhlaV9tZWRpYV92aWV3cyRwZXJjZW50YWdlX3JlcGx5IDwtIGhlaV9tZWRpYV92aWV3cyR0b3RhbF9yZXBseXMgLyBzdW0oaGVpX21lZGlhX3ZpZXdzJHRvdGFsX3JlcGx5cykgKiAxMDAKICAKICAjIENyZWF0aW5nIHRoZSBwaWUgY2hhcnQgZm9yIHZpZXdzCiAgaGVpX3BpZV9jaGFydF92aWV3cyA8LSBnZ3Bsb3QoaGVpX21lZGlhX3ZpZXdzLCBhZXMoeCA9ICIiLCB5ID0gcGVyY2VudGFnZV92aWV3LCBmaWxsID0gbWVkaWFfdHlwZSkpICsKICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsKICAgIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQgPSAwKSArCiAgICB0aGVtZV92b2lkKCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlKG1lZGlhX3R5cGUsICJcbiIsIHRvdGFsX3ZpZXdzLCAiKCIsIHJvdW5kKHBlcmNlbnRhZ2VfdmlldywgMSksICIlKSIpKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIGNvbG9yID0gIiNGRkZGRkYiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJub19tZWRpYSIgPSAiIzIxOTZGMyIsICJhbmltYXRlZF9naWYiID0gIiNFNjdFMjIiLCAicGhvdG8iID0gIiM4RTQ0QUQiLCAidmlkZW8iID0gIiMxMzhENzUiKSkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJWaWV3cyBmb3IgZWFjaCBtZWRpYSB0eXBlIC0iLCB0YXJnZXRfaWQpKQogIAogICMgQ3JlYXRpbmcgdGhlIHBpZSBjaGFydCBmb3IgbGlrZXMKICBoZWlfcGllX2NoYXJ0X2xpa2VzIDwtIGdncGxvdChoZWlfbWVkaWFfdmlld3MsIGFlcyh4ID0gIiIsIHkgPSBwZXJjZW50YWdlX3ZpZXcsIGZpbGwgPSBtZWRpYV90eXBlKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKwogICAgY29vcmRfcG9sYXIoInkiLCBzdGFydCA9IDApICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUobWVkaWFfdHlwZSwgIlxuIiwgdG90YWxfdmlld3MsICIoIiwgcm91bmQocGVyY2VudGFnZV92aWV3LCAxKSwgIiUpIikpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgY29sb3IgPSAiI0ZGRkZGRiIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIm5vX21lZGlhIiA9ICIjRTkxRTYzIiwgImFuaW1hdGVkX2dpZiIgPSAiIzRBMTQ4QyIsICJwaG90byIgPSAiIzkwQ0FGOSIsICJ2aWRlbyIgPSAiIzAwQkZBNSIpKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoIkxpa2VzIGZvciBlYWNoIG1lZGlhIHR5cGUgLSIsIHRhcmdldF9pZCkpCiAgCiAgIyBDcmVhdGluZyB0aGUgcGllIGNoYXJ0IGZvciByZXBseXMKICBoZWlfcGllX2NoYXJ0X3JlcGx5cyA8LSBnZ3Bsb3QoaGVpX21lZGlhX3ZpZXdzLCBhZXMoeCA9ICIiLCB5ID0gcGVyY2VudGFnZV92aWV3LCBmaWxsID0gbWVkaWFfdHlwZSkpICsKICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsKICAgIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQgPSAwKSArCiAgICB0aGVtZV92b2lkKCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlKG1lZGlhX3R5cGUsICJcbiIsIHRvdGFsX3ZpZXdzLCAiKCIsIHJvdW5kKHBlcmNlbnRhZ2VfdmlldywgMSksICIlKSIpKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIGNvbG9yID0gIiNGRkZGRkYiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJub19tZWRpYSIgPSAiIzY2NjYwMCIsICJhbmltYXRlZF9naWYiID0gIiM5OUNDQ0MiLCAicGhvdG8iID0gIiM5OTY2Q0MiLCAidmlkZW8iID0gIiMzMzAwMDAiKSkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJMaWtlcyBmb3IgZWFjaCBtZWRpYSB0eXBlIC0iLCB0YXJnZXRfaWQpKQogIAogICMgUHJpbnQgdGhlIHBpZSBjaGFydHMKICBwcmludChoZWlfcGllX2NoYXJ0X3ZpZXdzKQogIHByaW50KGhlaV9waWVfY2hhcnRfbGlrZXMpCiAgcHJpbnQoaGVpX3BpZV9jaGFydF9yZXBseXMpCn0KYGBgCgojIFBsb3Qgb2YgcGllY2hhcnRzIGZvciBlYWNoIEhFSQoKYGBge3J9CnBpZV9tYWtlcigpCnBpZV9tYWtlcigiZXBmbC5jc3YiKQpwaWVfbWFrZXIoImdvZS5jc3YiKQpwaWVfbWFrZXIoImhhcnZhcmQuY3N2IikKcGllX21ha2VyKCJsZWljZXN0ZXIuY3N2IikKcGllX21ha2VyKCJtYW5jaGVzdGVyLmNzdiIpCnBpZV9tYWtlcigibWl0LmNzdiIpCnBpZV9tYWtlcigic2IuY3N2IikKcGllX21ha2VyKCJzdGFuZm9yZC5jc3YiKQpwaWVfbWFrZXIoInRyaW5pdHkuY3N2IikKcGllX21ha2VyKCJ3di5jc3YiKQpwaWVfbWFrZXIoInlhbGUuY3N2IikKYGBgCgpgYGB7cn0KIyBDYWxjdWxhdGlvbiBvZiBsaWtlX3JhdGlvIGFuZCByZXBseXNfcmF0aW8gcGVyY2VudGFnZXMKcmF0aW9zX3R3ZWV0c190YWJsZSA8LSB0b3RhbF90d2VldHNfc3RhdHMgJT4lCiAgbXV0YXRlKGxpa2VfcmF0aW8gPSB0b3RhbF9saWtlcyAvIHRvdGFsX3ZpZXdzICogMTAwLAogICAgICAgICByZXBseXNfcmF0aW8gPSB0b3RhbF9yZXBseXMgLyB0b3RhbF92aWV3cyAqIDEwMCkKCiMgQ3JlYXRpb24gb2YgbmV3IHRhYmxlIHdpdGggZWFjaCBIRUksIGxpa2VfcmF0aW8sIGFuZCByZXBseXNfcmF0aW8gCmhlaV90d2VldHNfcmF0aW9zIDwtIHJhdGlvc190d2VldHNfdGFibGUgJT4lCiAgc2VsZWN0KGlkLCBsaWtlX3JhdGlvLCByZXBseXNfcmF0aW8pICU+JQogIGRpc3RpbmN0KCkKCnByaW50KGhlaV90d2VldHNfcmF0aW9zKQpgYGAKCiMgUGxvdCBmb3IgbGlrZV9yYXRpbyBhbmQgcmVwbHlzX3JhdGlvIGZvciBlYWNoIEhFSQoKYGBge3J9CmdncGxvdChoZWlfdHdlZXRzX3JhdGlvcywgYWVzKHggPSBpZCkpICsKICBnZW9tX2JhcihhZXMoeSA9IGxpa2VfcmF0aW8sIGZpbGwgPSAiTGlrZSBSYXRpbyIpLCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSByZXBseXNfcmF0aW8sIGZpbGwgPSAiUmVwbHlzIFJhdGlvIiksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKHkgPSBsaWtlX3JhdGlvLCBsYWJlbCA9IHJvdW5kKGxpa2VfcmF0aW8sIDIpKSwgdmp1c3QgPSAtMC41LCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgc2l6ZSA9IDMsIGNvbG9yID0gIiMwMDAwMDAiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gcmVwbHlzX3JhdGlvLCBsYWJlbCA9IHJvdW5kKHJlcGx5c19yYXRpbywgMikpLCB2anVzdCA9IC0wLjUsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCBzaXplID0gMywgY29sb3IgPSAiI0ZGRkZGRiIpICsKICBsYWJzKHRpdGxlID0gIkxpa2UgYW5kIFJlcGx5cyBSYXRpb3MgYnkgSEVJIiwKICAgICAgIHggPSAiSEVJIiwKICAgICAgIHkgPSAiUmF0aW8gKCUpIiwKICAgICAgIGZpbGwgPSAiTWV0cmljIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkxpa2UgUmF0aW8iID0gIiMyMTk2RjMiLCAiUmVwbHlzIFJhdGlvIiA9ICIjRjQ0MzM2IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEpKQpgYGAKCmBgYHtyfQojIFRhYmxlIHdpdGggYXZlcmFnZXMgb2Ygdmlld3MsIGxpa2VzLCByZXR3ZWV0cyBhbmQgcmVwbHlzCnR5cGVzX29mX3R3ZWV0c19wZXJfdHdlZXQgPC0gdHlwZXNfb2ZfdHdlZXRzICU+JQogICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieShpZCwgbWVkaWFfdHlwZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShhdmdfdmlld3MgPSBtZWFuKHRvdGFsX3ZpZXdzIC8gY291bnQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXZnX2xpa2VzID0gbWVhbih0b3RhbF9saWtlcyAvIGNvdW50KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF2Z19yZXR3ZWV0cyA9IG1lYW4odG90YWxfcmV0d2VldHMgLyBjb3VudCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdmdfcmVwbHlzID0gbWVhbih0b3RhbF9yZXBseXMgLyBjb3VudCkpCgpwcmludCh0eXBlc19vZl90d2VldHNfcGVyX3R3ZWV0KQpgYGAKCmBgYHtyfQojIEdyb3VwaW5nIGJ5IEhFSSBhbmQgY2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2UgdmFsdWVzIG9mIHZpZXdzLCBsaWtlcyBhbmQgcmVwbHlzIGFjcm9zcyBhbGwgbWVkaWEgdHlwZXMKdG90YWxfYXZlcmFnZV9zdGF0cyA8LSB0eXBlc19vZl90d2VldHNfcGVyX3R3ZWV0ICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBzdW1tYXJpc2UoYXZnX3ZpZXdzID0gc3VtKGF2Z192aWV3cyksCiAgICAgICAgICAgIGF2Z19saWtlcyA9IHN1bShhdmdfbGlrZXMpLAogICAgICAgICAgICBhdmdfcmVwbHlzID0gc3VtKGF2Z19yZXBseXMpKQoKcHJpbnQodG90YWxfYXZlcmFnZV9zdGF0cykKYGBgCgpgYGB7cn0KIyBDYWxjdWxhdGlvbiBvZiBsaWtlX3JhdGlvIGFuZCByZXBseXNfcmF0aW8gcGVyY2VudGFnZXMKcmF0aW9zX2F2ZXJhZ2VfdGFibGUgPC0gdG90YWxfYXZlcmFnZV9zdGF0cyAlPiUKICBtdXRhdGUobGlrZV9yYXRpbyA9IGF2Z19saWtlcyAvIGF2Z192aWV3cyAqIDEwMCwKICAgICAgICAgcmVwbHlzX3JhdGlvID0gYXZnX3JlcGx5cyAvIGF2Z192aWV3cyAqIDEwMCkKCiMgQ3JlYXRpb24gb2YgbmV3IHRhYmxlIHdpdGggZWFjaCBIRUksIGxpa2VfcmF0aW8sIGFuZCByZXBseXNfcmF0aW8gCmhlaV9hdmVyYWdlX3JhdGlvcyA8LSByYXRpb3NfYXZlcmFnZV90YWJsZSAlPiUKICBzZWxlY3QoaWQsIGxpa2VfcmF0aW8sIHJlcGx5c19yYXRpbykgJT4lCiAgZGlzdGluY3QoKQoKcHJpbnQoaGVpX2F2ZXJhZ2VfcmF0aW9zKQpgYGAKCiMgUGxvdCBmb3IgbGlrZV9yYXRpbyBhbmQgcmVwbHlzX3JhdGlvIGZvciBlYWNoIEhFSQoKYGBge3J9CmdncGxvdChoZWlfYXZlcmFnZV9yYXRpb3MsIGFlcyh4ID0gaWQpKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSBsaWtlX3JhdGlvLCBmaWxsID0gIkxpa2UgUmF0aW8iKSwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fYmFyKGFlcyh5ID0gcmVwbHlzX3JhdGlvLCBmaWxsID0gIlJlcGx5cyBSYXRpbyIpLCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gbGlrZV9yYXRpbywgbGFiZWwgPSByb3VuZChsaWtlX3JhdGlvLCAyKSksIHZqdXN0ID0gLTAuNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHNpemUgPSAzLCBjb2xvciA9ICIjMDAwMDAwIikgKwogIGdlb21fdGV4dChhZXMoeSA9IHJlcGx5c19yYXRpbywgbGFiZWwgPSByb3VuZChyZXBseXNfcmF0aW8sIDIpKSwgdmp1c3QgPSAtMC41LCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgc2l6ZSA9IDMsIGNvbG9yID0gIiNGRkZGRkYiKSArCiAgbGFicyh0aXRsZSA9ICJMaWtlIGFuZCBSZXBseXMgUmF0aW9zIGJ5IEhFSSIsCiAgICAgICB4ID0gIkhFSSIsCiAgICAgICB5ID0gIlJhdGlvICglKSIsCiAgICAgICBmaWxsID0gIk1ldHJpYyIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJMaWtlIFJhdGlvIiA9ICIjMzMwMDY2IiwgIlJlcGx5cyBSYXRpbyIgPSAiI0ZGNjY2NiIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkKYGBgCgojIEhhc2h0YWdzCgpgYGB7cn0KIyBUYWJsZSB3aXRoIG51bWJlciBvZiB1bmlxdWUgaGFzaHRhZ3MKdW5pcXVlX2hhc2h0YWdzIDwtIGRhdGFfdHdlZXRzICU+JQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JQogICAgICAgICAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZV9oYXNodGFncyA9IGxlbmd0aCh1bmlxdWUoaGFzaHRhZ3MpKSkKCnByaW50KHVuaXF1ZV9oYXNodGFncykKYGBgCgojIFBsb3QgZm9yIHRoZSBjb3VudCBvZiB1bmlxdWUgaGFzaHRhZ3MgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdCh1bmlxdWVfaGFzaHRhZ3MkdW5pcXVlX2hhc2h0YWdzLAogICAgICAgIG5hbWVzLmFyZyA9IHVuaXF1ZV9oYXNodGFncyRpZCwKICAgICAgICBtYWluID0gIlVuaXF1ZSBIYXNodGFncyBmb3IgRWFjaCBIRUkiLAogICAgICAgIHhsYWIgPSAiSEVJIiwKICAgICAgICB5bGFiID0gIkNvdW50IG9mIFVuaXF1ZSBIYXNodGFncyIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHVuaXF1ZV9oYXNodGFncyR1bmlxdWVfaGFzaHRhZ3MpICsgNTApLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sPSAiIzE2QTA4NSIpCgp0ZXh0KHggPSBiYXJwbG90KHVuaXF1ZV9oYXNodGFncyR1bmlxdWVfaGFzaHRhZ3MsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IHVuaXF1ZV9oYXNodGFncyR1bmlxdWVfaGFzaHRhZ3MsCiAgICAgbGFiZWxzID0gcm91bmQodW5pcXVlX2hhc2h0YWdzJHVuaXF1ZV9oYXNodGFncywgMiksCiAgICAgcG9zID0gMykKYGBgCgojIEhlYXRtYXBzCgpgYGB7cn0KIyBDcmVhdGUgY29sdW1uIGhvdXIgZnJvbSBjcmVhdGVkX2F0CmRhdGFfdHdlZXRzX2RheXMkY3JlYXRlZF9ob3VyIDwtIGFzLm51bWVyaWMoZm9ybWF0KGRhdGFfdHdlZXRzX2RheXMkY3JlYXRlZF9hdCwgIiVIIikpCmBgYAoKIyBGdW5jdGlvbiB0byBwbG90IGhlYXRtYXAgZm9yIHZhcmlvdXMgSEVJcwoKYGBge3J9CmhlYXRtYXBfbWFrZXIgPC0gZnVuY3Rpb24odGFyZ2V0X2lkID0gImR1a2UuY3N2Iil7CiAgIyBGaWx0ZXJpbmcgZGF0YSBmb3IgdGhlIHNwZWNpZmljIEhFSQogIHRhcmdldF9kYXRhIDwtIGRhdGFfdHdlZXRzX2RheXMgJT4lCiAgICBmaWx0ZXIoaWQgPT0gdGFyZ2V0X2lkKQogIAogICMgR3JvdXBpbmcgYnkgZGF5IG9mIHRoZSB3ZWVrIGFuZCBob3VyLCBhbmQgY291bnRpbmcgdGhlIG51bWJlciBvZiB0d2VldHMKICB0d2VldF9jb3VudHMgPC0gdGFyZ2V0X2RhdGEgJT4lCiAgICBncm91cF9ieShkYXlfb2Zfd2VlaywgY3JlYXRlZF9ob3VyKSAlPiUKICAgIHN1bW1hcmlzZShudW1fdHdlZXRzID0gbigpKQogIAogICMgUGxvdHRpbmcgaGVhdG1hcAogIGdncGxvdCh0d2VldF9jb3VudHMsIGFlcyh4ID0gZGF5X29mX3dlZWssIHkgPSBjcmVhdGVkX2hvdXIsIGZpbGwgPSBudW1fdHdlZXRzKSkgKwogICAgZ2VvbV90aWxlKCkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoIlR3ZWV0IEhlYXRtYXAgZm9yIiwgdGFyZ2V0X2lkKSwKICAgICAgICAgeCA9ICJEYXkgb2YgdGhlIHdlZWsiLAogICAgICAgICB5ID0gIkhvdXIgb2YgdGhlIGRheSIpCn0KCmhlYXRtYXBfbWFrZXIoKQpoZWF0bWFwX21ha2VyKCJlcGZsLmNzdiIpCmhlYXRtYXBfbWFrZXIoImdvZS5jc3YiKQpoZWF0bWFwX21ha2VyKCJoYXJ2YXJkLmNzdiIpCmhlYXRtYXBfbWFrZXIoImxlaWNlc3Rlci5jc3YiKQpoZWF0bWFwX21ha2VyKCJtYW5jaGVzdGVyLmNzdiIpCmhlYXRtYXBfbWFrZXIoIm1pdC5jc3YiKQpoZWF0bWFwX21ha2VyKCJzYi5jc3YiKQpoZWF0bWFwX21ha2VyKCJzdGFuZm9yZC5jc3YiKQpoZWF0bWFwX21ha2VyKCJ0cmluaXR5LmNzdiIpCmhlYXRtYXBfbWFrZXIoInd2LmNzdiIpCmhlYXRtYXBfbWFrZXIoInlhbGUuY3N2IikKYGBgCgojIFRleHQKCmBgYHtyfQpkYXRhX3R3ZWV0c19jb250ZW50IDwtIGRhdGFfdHdlZXRzICU+JQogICAgICAgICAgICBzZWxlY3QoaWQsIHRleHQpCgojIENvdW50aW5nIG51bWJlciBvZiB3b3JkcwpkYXRhX3R3ZWV0c19jb250ZW50IDwtIGRhdGFfdHdlZXRzX2NvbnRlbnQgJT4lCiAgbXV0YXRlKG51bV93b3JkcyA9IGxlbmd0aHMoc3Ryc3BsaXQodGV4dCwgIlxccysiKSkpCgpwcmludChkYXRhX3R3ZWV0c19jb250ZW50KQoKIyBHcm91cGluZyBieSBIRUkgYW5kIGNhbGN1bGF0ZSBhdmVyYWdlLCBtaW5pbXVtLCBhbmQgbWF4aW11bSB2YWx1ZXMgb2YgbnVtYmVyIG9mIHdvcmRzCmRhdGFfdHdlZXRzX2NvbnRlbnRfbWV0cmljcyA8LSBkYXRhX3R3ZWV0c19jb250ZW50ICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBzdW1tYXJpc2UoYXZlcmFnZV9udW1fd29yZHMgPSBtZWFuKG51bV93b3JkcyksCiAgICAgICAgICAgIG1pbl9udW1fd29yZHMgPSBtaW4obnVtX3dvcmRzKSwKICAgICAgICAgICAgbWF4X251bV93b3JkcyA9IG1heChudW1fd29yZHMpKQpwcmludChkYXRhX3R3ZWV0c19jb250ZW50X21ldHJpY3MpCmBgYAoKIyBQbG90IGZvciB0aGUgYXZlcmFnZSwgbWF4aW11bSBhbmQgbWluaW11bSB2YWx1ZXMgb2Ygd29yZHMgZm9yIGVhY2ggSEVJCgpgYGB7cn0KZ2dwbG90KGRhdGFfdHdlZXRzX2NvbnRlbnRfbWV0cmljcywgYWVzKHggPSBpZCwgeSA9IGF2ZXJhZ2VfbnVtX3dvcmRzKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gIkF2ZXJhZ2UiKSkgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBtaW5fbnVtX3dvcmRzLCB5bWF4ID0gbWF4X251bV93b3JkcywgY29sb3IgPSAiUmFuZ2UiKSwgd2lkdGggPSAwLjIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiQXZlcmFnZSIgPSAiIzE5NzZEMiIsICJSYW5nZSIgPSAiI0VGNTM1MCIpKSArCiAgbGFicyh0aXRsZSA9ICJXb3JkIENvdW50IFN1bW1hcnkgYnkgSEVJIiwKICAgICAgIHggPSAiSEVJIiwKICAgICAgIHkgPSAiTnVtYmVyIG9mIFdvcmRzIiwKICAgICAgIGNvbG9yID0gIk1ldHJpYyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEpKQpgYGAKCiMgTm93IGNvbW1lbnRzIAoKYGBge3J9CmRhdGFfcmVwbGllcyA8LSBkYXRhW2RhdGEkdHlwZSA9PSAiUmVwbHkiLCBdCgpkYXRhX3JlcGxpZXMKYGBgCgojIEludGVyYWN0aW9ucyB0byByZXBseXMKCmBgYHtyfQojIFRhYmxlIGNvbnRhaW5pbmcgdmlld3MsIGxpa2VzLCByZXR3ZWV0cyBhbmQgcmVwbHlzIGZvciBlYWNoIG1lZGlhIHR5cGUgZm9yIGVhY2ggSEVJCnR5cGVzX29mX3JlcGxpZXMgPC0gZGF0YV9yZXBsaWVzICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkLCBtZWRpYV90eXBlKSAlPiUKICAgICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsX3ZpZXdzID0gc3VtKHZpZXdfY291bnQsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsX2xpa2VzID0gc3VtKGZhdm9yaXRlX2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9yZXR3ZWV0cyA9IHN1bShyZXR3ZWV0X2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9yZXBseXMgPSBzdW0ocmVwbHlfY291bnQsIG5hLnJtID0gVFJVRSkpCiAgICAgICAgICAgICAgICAgICAgICAgIApwcmludCh0eXBlc19vZl9yZXBsaWVzKSAgICAgICAgICAgICAgICAgICAgICAgIApgYGAKCmBgYHtyfQojIEdyb3VwaW5nIGJ5IEhFSSBhbmQgY2FsY3VsYXRpbmcgdGhlIHRvdGFsIHZhbHVlcyBvZiB2aWV3cywgbGlrZXMgYW5kIHJlcGx5cyBhY3Jvc3MgYWxsIG1lZGlhIHR5cGVzCnRvdGFsX3JlcGxpZXNfc3RhdHMgPC0gdHlwZXNfb2ZfcmVwbGllcyAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKHRvdGFsX3ZpZXdzID0gc3VtKHRvdGFsX3ZpZXdzKSwKICAgICAgICAgICAgdG90YWxfbGlrZXMgPSBzdW0odG90YWxfbGlrZXMpLAogICAgICAgICAgICB0b3RhbF9yZXBseXMgPSBzdW0odG90YWxfcmVwbHlzKSkKCnByaW50KHRvdGFsX3JlcGxpZXNfc3RhdHMpCmBgYAoKYGBge3J9CiMgQ2FsY3VsYXRpb24gb2YgbGlrZV9yYXRpbyBhbmQgcmVwbHlzX3JhdGlvIHBlcmNlbnRhZ2VzCnJhdGlvc19yZXBsaWVzX3RhYmxlIDwtIHRvdGFsX3JlcGxpZXNfc3RhdHMgJT4lCiAgbXV0YXRlKGxpa2VfcmF0aW8gPSB0b3RhbF9saWtlcyAvIHRvdGFsX3ZpZXdzICogMTAwLAogICAgICAgICByZXBseXNfcmF0aW8gPSB0b3RhbF9yZXBseXMgLyB0b3RhbF92aWV3cyAqIDEwMCkKCiMgQ3JlYXRpb24gb2YgbmV3IHRhYmxlIHdpdGggZWFjaCBIRUksIGxpa2VfcmF0aW8sIGFuZCByZXBseXNfcmF0aW8gCmhlaV9yZXBsaWVzX3JhdGlvcyA8LSByYXRpb3NfcmVwbGllc190YWJsZSAlPiUKICBzZWxlY3QoaWQsIGxpa2VfcmF0aW8sIHJlcGx5c19yYXRpbykgJT4lCiAgZGlzdGluY3QoKQoKcHJpbnQoaGVpX3JlcGxpZXNfcmF0aW9zKQpgYGAKCiMgQ2x1c3RlcnMKCmBgYHtyfQojIENyZWF0aW5nIHRhYmxlIGZvciBjbHVzdGVyIGFsZ29yaXRobXMKCiMgSm9pbmluZyBhdHRyaWJ1dGUgY291bnQgKG51bWJlciBvZiB0d2VldHMpIGFuZCB1bmlxdWVfaGFzaHRhZ3MgKG51bWJlciBvZiB1bmlxdWUgaGFzaHRhZ3MpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShzZWxlY3QodW5pcXVlX2hhc2h0YWdzLCBpZCwgdW5pcXVlX2hhc2h0YWdzKSwgc2VsZWN0KG51bWJlcl90d2VldHMsIGlkLCBjb3VudCksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z190d2VldHNfcGVyX2RheXMgKGF2ZXJhZ2Ugb2YgdHdlZXRzIHBlciBkYXkpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QodHdlZXRzX3Blcl9kYXksIGlkLCBhdmdfdHdlZXRzX3Blcl9kYXlzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgYXZnX3R3ZWV0c19wZXJfd2Vla3MgKGF2ZXJhZ2Ugb2YgdHdlZXRzIHBlciB3ZWVrKSBwZXIgSEVJCmNsdXN0ZXJfdGFibGUgPC0gbWVyZ2UoY2x1c3Rlcl90YWJsZSwgc2VsZWN0KHR3ZWV0c19wZXJfd2VlaywgaWQsIGF2Z190d2VldHNfcGVyX3dlZWtzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgYXZnX3R3ZWV0c19pbl9hY2FkZW1pY190aW1lIChhdmVyYWdlIG9mIHR3ZWV0cyBkdXJpbmcgYWNhZGVtaWMgdGltZSkgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChkYXRhX3R3ZWV0c19hY2FkZW1pYywgaWQsIGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z190d2VldHNfaW5fdmFjYXRpb25fdGltZSAoYXZlcmFnZSBvZiB0d2VldHMgZHVyaW5nIHZhY2F0aW9uIHRpbWUpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QoZGF0YV90d2VldHNfdmFjYXRpb25zLCBpZCwgYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgdG90YWxfdmlld3MgKHRvdGFsIG51bWJlciBvZiB2aWV3cyksIHRvdGFsX2xpa2VzICh0b3RhbCBudW1iZXIgb2YgbGlrZXMpIGFuZCB0b3RhbF9yZXBseXMgKHRvdGFsIG51bWJlciBvZiByZXBsaWVzKSBwZXIgSEVJCmNsdXN0ZXJfdGFibGUgPC0gbWVyZ2UoY2x1c3Rlcl90YWJsZSwgc2VsZWN0KHRvdGFsX3R3ZWV0c19zdGF0cywgaWQsIHRvdGFsX3ZpZXdzLCB0b3RhbF9saWtlcywgdG90YWxfcmVwbHlzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgbGlrZV9yYXRpbyAocmF0aW8gb2YgdG90YWwgbnVtYmVyIG9mIGxpa2VzKSBhbmQgcmVwbHlzX3JhdGlvIChyYXRpbyBvZiB0b3RhbCBudW1iZXIgb2YgcmVwbGllcykgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChoZWlfdHdlZXRzX3JhdGlvcywgaWQsIGxpa2VfcmF0aW8sIHJlcGx5c19yYXRpbyksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCmNsdXN0ZXJfdGFibGUgPC0gY2x1c3Rlcl90YWJsZSAlPiUKICByZW5hbWUodG90YWxfbGlrZV9yYXRpbyA9IGxpa2VfcmF0aW8sIAogICAgICAgICB0b3RhbF9yZXBseXNfcmF0aW8gPSByZXBseXNfcmF0aW8pCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z192aWV3cyAoYXZlcmFnZSBudW1iZXIgb2Ygdmlld3MpLCBhdmdfbGlrZXMgKGF2ZXJhZ2UgbnVtYmVyIG9mIGxpa2VzKSBhbmQgYXZnX3JlcGx5cyAoYXZlcmFnZSBudW1iZXIgb2YgcmVwbGllcykgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdCh0b3RhbF9hdmVyYWdlX3N0YXRzLCBpZCwgYXZnX3ZpZXdzLCBhdmdfbGlrZXMsIGF2Z19yZXBseXMpLCBieSA9ICJpZCIsIGFsbD1UUlVFKQoKIyBKb2luaW5nIGF0dHJpYnV0ZSBsaWtlX3JhdGlvIChyYXRpbyBvZiBhdmVyYWdlIG51bWJlciBvZiBsaWtlcykgYW5kIHJlcGx5c19yYXRpbyAocmF0aW8gb2YgYXZlcmFnZSBudW1iZXIgb2YgcmVwbGllcykgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChoZWlfYXZlcmFnZV9yYXRpb3MsIGlkLCBsaWtlX3JhdGlvLCByZXBseXNfcmF0aW8pLCBieSA9ICJpZCIsIGFsbD1UUlVFKQpjbHVzdGVyX3RhYmxlIDwtIGNsdXN0ZXJfdGFibGUgJT4lCiAgcmVuYW1lKGF2Z19saWtlX3JhdGlvID0gbGlrZV9yYXRpbywgCiAgICAgICAgIGF2Z19yZXBseXNfcmF0aW8gPSByZXBseXNfcmF0aW8pCgpwcmludChjbHVzdGVyX3RhYmxlKQpgYGAKCiMgRnVuY3Rpb24gZm9yIGNsdXN0ZXIgbWV0aG9kCgpgYGB7cn0KY2x1c3Rlcl9tYWtlciA8LSBmdW5jdGlvbihzZWVkID0gMTIzLCBudW1fY2x1c3RlcnMgPSAzLCB0YWJsZSl7CiAgc2V0LnNlZWQoMTIzKQogIAogICMgRXhjbHVkaW5nIGlkIGNvbHVtbiBmb3IgY2x1c3RlcmluZwogIGNsdXN0ZXJfZGF0YSA8LSBzZWxlY3QodGFibGUsIC1pZCkKICAKICAjIFNjYWxpbmcgdGhlIGRhdGEgZm9yIGttZWFucyBtZXRob2QKICBzY2FsZWRfZGF0YSA8LSBzY2FsZShjbHVzdGVyX2RhdGEpCiAgCiAga21lYW5zX3Jlc3VsdCA8LSBrbWVhbnMoc2NhbGVkX2RhdGEsIGNlbnRlcnMgPSBudW1fY2x1c3RlcnMpCiAgCiAgcHJpbnQoa21lYW5zX3Jlc3VsdCRjZW50ZXJzKQogIHByaW50KGttZWFuc19yZXN1bHQkY2x1c3RlcikKICAKICByZXR1cm4oa21lYW5zX3Jlc3VsdCkKfQpgYGAKCiMgRnVuY3Rpb24gdG8gYWRkIGlkcyB0byBiZXR0ZXIgdmlzdWFsaXplIHJlc3VsdHMKCmBgYHtyfQpjbHVzdGVyX2lkX21ha2VyIDwtIGZ1bmN0aW9uKGttZWFuc19yZXN1bHQsIHRhYmxlKXsKICAjIE1lcmdpbmcgdGhlIGNsdXN0ZXIgYXNzaWdubWVudHMgd2l0aCB0aGUgb3JpZ2luYWwgZGF0YQogIGNsdXN0ZXJfYXNzaWdubWVudHMgPC0gZGF0YS5mcmFtZShpZCA9IHRhYmxlJGlkLCBjbHVzdGVyID0ga21lYW5zX3Jlc3VsdCRjbHVzdGVyKQoKICBwcmludChjbHVzdGVyX2Fzc2lnbm1lbnRzKQogIHBsb3Qoa21lYW5zX3Jlc3VsdCRjbHVzdGVyKQp9CmBgYAoKIyBUaHJlZSBjbHVzdGVycyB3aXRoIHNlZWQgMTIzCgpgYGB7cn0KY2x1c3Rlcl8xMjNfMyA8LSBjbHVzdGVyX21ha2VyKHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKY2x1c3Rlcl9pZF9tYWtlcihjbHVzdGVyXzEyM18zLCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmBgYAoKIyBTaXggY2x1c3RlcnMgd2l0aCBzZWVkIDEyMwoKYGBge3J9CmNsdXN0ZXJfMTIzXzYgPC0gY2x1c3Rlcl9tYWtlcihudW1fY2x1c3RlcnMgPSA2LCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmNsdXN0ZXJfaWRfbWFrZXIoY2x1c3Rlcl8xMjNfNiwgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpgYGAKCiMgRm91ciBjbHVzdGVycyB3aXRoIHNlZWQgNDg1NQoKYGBge3J9CmNsdXN0ZXJfMTIzXzMgPC0gY2x1c3Rlcl9tYWtlcihzZWVkID0gNDg1NSwgbnVtX2NsdXN0ZXJzID0gNCwgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpjbHVzdGVyX2lkX21ha2VyKGNsdXN0ZXJfMTIzXzMsIHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKYGBgCgojIFNpeCBjbHVzdGVycyB3aXRoIHNlZWQgNDg1NQoKYGBge3J9CmNsdXN0ZXJfMTIzXzYgPC0gY2x1c3Rlcl9tYWtlcihzZWVkID0gNDg1NSwgbnVtX2NsdXN0ZXJzID0gNiwgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpjbHVzdGVyX2lkX21ha2VyKGNsdXN0ZXJfMTIzXzYsIHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKYGBgCgo=